/*
 * Licensed under the MIT License
 * 
 * Copyright (c) 2011 Kay Kreuning
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * http://code.google.com/p/hyves-as3-sdk/
 */
package nl.hyves.api.net
{
	import nl.hyves.api.core.IHyvesAPI;
	import nl.hyves.api.errors.HyvesError;
	import nl.hyves.api.methods.Albums;
	import nl.hyves.api.methods.Media;
	import nl.hyves.api.methods.options.UploadParameters;
	import nl.hyves.api.utils.MultipartUtil;

	import com.adobe.cairngorm.contract.Contract;
	import com.adobe.images.JPGEncoder;
	import com.adobe.serialization.json.JSONDecoder;

	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.net.URLRequestMethod;
	import flash.utils.ByteArray;
	import flash.utils.Timer;



	public class HyvesUpload extends HyvesRequest implements IHyvesRequest//, IHyvesResponse
	{
		private static const DEFAULT_UPLOAD_FILENAME:String = 'Uploaded with Hyves-AS3-SDK';
		private static const POLL_DELAY_MILLISECONDS:Number = 500;
		
		private var image:ByteArray;
		private var timer:Timer;
		private var uploadToken:String;
		private var uploadURL:String;
		private var cacheBusterIndex:int = 1;
		private var mediaid:String;
		private var encoder:JPGEncoder;

		public function HyvesUpload(service:IHyvesAPI, image:*, callback:Function, parameters:Object)
		{
			parameters ||= new Object();
			
			Contract.precondition(service != null);
			Contract.precondition(image != null && (image is ByteArray || image is Bitmap || image is BitmapData));
			Contract.precondition(callback != null);
			Contract.precondition(parameters != null && (parameters is Object || parameters is UploadParameters));

			if (!parameters.title)
			{
				parameters.title = '';
			}

			if (!parameters.description)
			{
				parameters.description = '';
			}

			super(service, parameters, callback);

			this.image = getEncodedByteArray(image);

			timer = new Timer(POLL_DELAY_MILLISECONDS);
			timer.addEventListener(TimerEvent.TIMER, pollStatus);
		}

		override public function load():void
		{
			if (hasRequiredMethods())
			{
				service.call(Media.getUploadToken(), onUploadToken);
			}
			else
			{
				throw new HyvesError('Uploading requires certain API method that are not available to the current accesstoken.');
			}
		}

		private function onUploadToken(response:IHyvesResponse):void
		{
			uploadToken = response.data.token;
			uploadURL = 'http://' + response.data.ip;

			var variables:Object = new Object();

			if (parameters)
			{
				if (parameters.geodata)
				{
					variables.geodata = parameters.geodata.x + ',' + parameters.geodata.y;
				}

				if (parameters.angle)
				{
					variables.angle = parameters.angle;
				}
			}

			// Security.allowDomain('http://' + uploadIP);

			var request:URLRequest = new URLRequest(uploadURL + '/upload?token=' + encodeURIComponent(uploadToken));
			request.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + MultipartUtil.getBoundary()));
			request.method = URLRequestMethod.POST;
			request.data = MultipartUtil.getPostData(parameters.title || DEFAULT_UPLOAD_FILENAME, image, variables);

			var loader:URLLoader = new URLLoader();
			loader.dataFormat = URLLoaderDataFormat.BINARY;
			loader.addEventListener(Event.COMPLETE, onLoaderComplete);
			loader.load(request);

			timer.start();

			pollStatus();
		}

		private function onLoaderComplete(event:Event):void
		{
			event.currentTarget.removeEventListener(Event.COMPLETE, onLoaderComplete);

			pollStatus();
		}

		private function pollStatus(event:TimerEvent = null):void
		{
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onStatusComplete);
			loader.load(new URLRequest(uploadURL + '/status?token=' + encodeURIComponent(uploadToken) + '&callback=' + generateCacheBuster()));
		}

		private function onStatusComplete(event:Event):void
		{
			if (timer.running)
			{
				// Strip off padding added by cacheBuster:
				var response:String = event.target.data;
				response = /(?<=^_jsreqCb.cb_\d\().+(?<!\)$)/.exec(response);

				var json:Object = new JSONDecoder(response, true).getValue();

				if (json.data && json.data[uploadToken] && json.data[uploadToken][0] && json.data[uploadToken][0].currentstate)
				{
					var status:String = json.data[uploadToken][0].currentstate;

					if (status == 'done')
					{
						timer.removeEventListener(TimerEvent.TIMER, pollStatus);

						mediaid = json.data[uploadToken][0].done.mediaid;

						if (parameters.albumid)
						{
							service.call(Albums.addMedia(parameters.albumid, mediaid), onAddMedia);
						}
						else
						{
							onAddMedia();
						}

						timer.stop();
					}
				}
			}
		}

		private function onAddMedia(response:IHyvesResponse = null):void
		{
			// Keep FDT from complaining:
			response;

			if (parameters.title || parameters.description)
			{
				service.call(Media.update(mediaid, parameters.title, parameters.description), onUpdateMedia);
			}
			else
			{
				onUpdateMedia();
			}
		}

		private function onUpdateMedia(response:IHyvesResponse = null):void
		{
			// Keep FDT from complaining:
			response;

			service.call(Media.get(mediaid), onGetMedia, {ha_responsefield: 'fancylayouttag'});
		}

		private function onGetMedia(response:IHyvesResponse):void
		{
			callback(response);
		}

		private function getEncodedByteArray(image:*):ByteArray
		{
			if (image is ByteArray)
			{
				return image;
			}
			else if (image is BitmapData)
			{
				encoder ||= new JPGEncoder();

				return getEncodedByteArray(encoder.encode(image));
			}
			else if (image is Bitmap)
			{
				return getEncodedByteArray(image.bitmapData);
			}
			else
			{
				return null;
			}
		}

		private function hasRequiredMethods():Boolean
		{
			var methods:Vector.<String> = Vector.<String>([Media.GET_UPLOAD_TOKEN, Media.GET]);

			if (parameters.albumid)
			{
				methods.push(Albums.ADD_MEDIA);
			}

			if (parameters.title || parameters.description)
			{
				methods.push(Media.UPDATE);
			}

			return service.isLoggedIn(methods);
		}

		private function generateCacheBuster():String
		{
			return '_jsreqCb.cb_' + cacheBusterIndex++;
		}
	}
}